package restdom.dao.impl;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import restdom.dao.GenericIdResolver;

public class ReflectiveIdResolver<T, ID extends Serializable> implements
		GenericIdResolver<T, ID> {

	private Class<ID> idClass;

	private Method propertyGetter;

	private Method propertySetter;

	public ReflectiveIdResolver(Class<T> entityClass, Class<ID> idClass) {
		this(entityClass, idClass, "id");
	}

	public ReflectiveIdResolver(Class<T> entityClass, Class<ID> idClass,
			String propertyName) {
		if (entityClass == null) {
			throw new IllegalArgumentException(
					"Entity class must be specified");
		}
		if (idClass == null) {
			throw new IllegalArgumentException(
					"Identifier class must be specified");
		}
		this.idClass = idClass;
		if (propertyName == null) {
			throw new IllegalArgumentException(
					"ID property name must be specified");
		}
		try {
			this.propertySetter = entityClass.getMethod(
					getPropertySetter(propertyName), idClass);
			this.propertyGetter = entityClass
					.getMethod(getPropertyGetter(propertyName));
		} catch (SecurityException e) {
			throw new RuntimeException("Cannot use ID property getter or setter",
					e);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException("Cannot find ID property getter or setter",
					e);
		}
	}

	public ID getIdValue(T entity) {
		if (entity == null) {
			throw new RuntimeException("Cannot get ID value: entity is null");
		}
		try {
			return idClass.cast(propertyGetter.invoke(entity));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException("Failed to get ID value", e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Failed to get ID value", e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException("Failed to get ID value", e);
		}
	}

	public void setIdValue(T entity, ID id) {
		if (entity == null) {
			throw new RuntimeException("Cannot set ID value: entity is null");
		}
		try {
			propertySetter.invoke(entity, id);
		} catch (IllegalArgumentException e) {
			throw new RuntimeException("Failed to set ID value", e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Failed to set ID value", e);
		} catch (InvocationTargetException e) {
			throw new RuntimeException("Failed to set ID value", e);
		}
	}

	protected String getPropertySetter(String property) {
		return "set" + Character.toUpperCase(property.charAt(0))
				+ property.substring(1);
	}

	protected String getPropertyGetter(String property) {
		return "get" + Character.toUpperCase(property.charAt(0))
				+ property.substring(1);
	}

}
